package oauth

import (
	"bytes"
	"context"
	"fmt"
	"github.com/spf13/cast"
	"google.golang.org/grpc/metadata"
	"micro-oauth/datamodels/enum"
	"micro-oauth/service/vendor_account"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"micro-oauth/datamodels/autherror"
	"micro-oauth/datamodels/params"
	"micro-oauth/service/cache"
	"micro-oauth/utils"

	"micro-oauth/config"
	model "micro-oauth/datamodels"
	"micro-oauth/repo"
)

type OauthServiceImpl struct {
	repoService  repo.ServiceInterface
	cacheService cache.CacheServiceInterface
	jwtSecret    string
}



func NewOauthService(repoService repo.ServiceInterface, cacheService cache.CacheServiceInterface, jwtSecret string) OauthServiceInterface {
	return &OauthServiceImpl{
		repoService:  repoService,
		cacheService: cacheService,
		jwtSecret:    jwtSecret,
	}
}

func (s *OauthServiceImpl) RefreshToken(ctx context.Context, accessToken, refreshToken string) (*model.Token,autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	scopeId, ok := ctx.Value("scope_id").(uint64)
	if !ok {
		scopeId = 0
	}
	userId := ctx.Value("user_id").(uint64)
	tokenObj := s.repoService.GetToken(accessToken)
	if tokenObj == nil {
		return tokenObj,autherror.NotFound("can not find access token")
	}
	if tokenObj.PartnerId != pId || tokenObj.UserId != userId || tokenObj.ScopeId != scopeId {
		return tokenObj,autherror.PermissionDenied("no permission refresh this token")
	}
	if tokenObj.RefreshToken != refreshToken {
		return tokenObj,autherror.InvalidData("wrong refresh token")
	}
	client := s.repoService.GetClientByPrimaryId(tokenObj.ClientId)
	if client == nil {
		return tokenObj,autherror.InvalidData("can not find client")
	}
	now := time.Now()
	var validDuration = time.Duration(client.TokenExpireDuration) * time.Second
	if tokenObj.ExpiresIn.After(now) {
		if tokenObj.ExpiresIn.Sub(now) < validDuration {
			tokenObj.ExpiresIn = tokenObj.ExpiresIn.Add(validDuration)
			err := s.repoService.UpdateToken(tokenObj)
			if err != nil {
				return tokenObj,autherror.DBError(err.Error())
			}
		} else {
			return tokenObj,autherror.InvalidAction("token expire in is too big to refresh, please wait some time")
		}
	} else {
		return tokenObj,autherror.InvalidData("token has expired")
	}
	return tokenObj,nil
}

func (s *OauthServiceImpl) GetUserById(ctx context.Context, userId uint64) (*model.User, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	scopeId, ok := ctx.Value("scope_id").(uint64)
	if !ok {
		scopeId = 0
	}
	user := s.repoService.GetUserById(userId)
	if user == nil {
		return nil, autherror.NotFound("can not found user")
	}
	if user.PartnerId != pId {
		return nil, autherror.PermissionDenied("no partner permission")
	}
	if user.ScopeId != scopeId {
		return nil, autherror.PermissionDenied("no scope permission")
	}
	return user, nil
}

func (s *OauthServiceImpl) Check() error {
	return s.cacheService.Ping()
}

func (s *OauthServiceImpl) GetClientById(clientId string) (*model.Client, autherror.HexError) {
	client := s.repoService.GetClientById(clientId)
	if client == nil {
		return nil, autherror.NotFound("ClientId 未知！")
	}
	return client, nil
}

func (s *OauthServiceImpl) genBearerTokenForUser(client *model.Client, user *model.User, state, scope string, device *model.Device) (*model.Token, autherror.HexError) {
	if client.PartnerId != user.PartnerId {
		return nil, autherror.InvalidAction("client and user not belong to same partner")
	}
	expireTime := time.Now().Add(time.Duration(client.TokenExpireDuration) * time.Second)
	buf := bytes.NewBufferString(client.ClientId)
	buf.WriteString(strconv.Itoa(int(user.Id)))
	buf.WriteString(strconv.Itoa(int(time.Now().UnixNano())))
	accessToken := utils.GenUniqueToken(buf.String())
	refreshToken := utils.GenUniqueToken(buf.String() + "Refresh")
	token := &model.Token{
		GrantType:        model.Implicit,
		AccessToken:      accessToken,
		TokenType:        model.Bearer,
		ExpiresIn:        expireTime,
		RefreshExpiresIn: expireTime,
		RefreshToken:     refreshToken,
		RequestState:     state,
		UserId:           user.Id,
		ClientId:         client.Id,
		ScopeCode:        scope,
		ScopeId:          client.ScopeId,
		PartnerId:        user.PartnerId,
		TerminalId:       client.TerminalId,
		DeviceUUID:       device.UUID,
	}
	err := s.repoService.AddToken(token)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return token, nil
}

func (s *OauthServiceImpl) UserLogin(clientId, username, pwd, state, scope, redirectURI string, deviceUUID string) (*model.Client, *model.User, *model.Token, autherror.HexError) {
	client, err := s.GetClientById(clientId)
	if err != nil {
		return nil, nil, nil, err
	}
	if !client.ContainScope(scope) {
		return nil, nil, nil, autherror.NotFound("Invalid Scope")
	}
	if !client.ValidRedirectURI(redirectURI) {
		return nil, nil, nil, autherror.NotFound("Invalid RedirectURI")
	}
	// 检测登录设备是否合法的逻辑
	var device = new(model.Device)
	if client.Type == enum.PDAClient {
		if deviceUUID == "" {
			return nil, nil, nil, autherror.InvalidData("no device uuid")
		}
		device = s.repoService.GetDeviceByUUID(deviceUUID)
		if device == nil {
			return nil, nil, nil, autherror.NotFound("设备未注册！")
		}
		if device.PartnerId != client.PartnerId || device.ScopeId != client.ScopeId {
			return nil, nil, nil, autherror.PermissionDenied("租户和设备不匹配！")
		}
		if device.Status == enum.DeviceStatus_Init {
			return nil, nil, nil, autherror.PermissionDenied("该设备未经批准, 不可登录！")
		} else if device.Status == enum.DeviceStatus_Forbidden {
			return nil, nil, nil, autherror.PermissionDenied("该设备禁止登录！")
		}
	}
	user := s.repoService.GetUserByName(client.PartnerId, client.ScopeId, username)
	if user == nil {
		utils.LoginErrorCounter.WithLabelValues("NO_USER").Inc()
		err := autherror.NotFound("账户或密码错误！")
		return nil, nil, nil, err
	}
	ruleCfg := config.LoadPassWdRuleConfig()
	rule := &model.PassWordRule{}
	if ruleCfg.Enable {
		r,errR := s.repoService.GetPasswordRule()
		if errR != nil{
			return nil,nil,nil,autherror.DBError("访问失败")
		}
		rule = r

		// pre check password rule
		if rule != nil && rule.LoginStatus == 2 {
			if existed := s.cacheService.ExistedLockedUser(user.Id);existed{
				return nil,nil,nil,autherror.NewHexError(string(enum.UserLockedError),"该账户已经于五分钟内输入密码错误三次,已被锁定十分钟")
			}
		}

		if rule != nil && len(rule.PassWordExpire) > 0 {
			if  user.PasswordStatus == 1{
				now := time.Now().Unix()
				local,_:= time.LoadLocation("Asia/Shanghai")
				expire,errT := time.ParseInLocation("2006-01-02 15:04:05",rule.PassWordExpire,local)
				if errT != nil{
					return nil,nil,nil,autherror.HexErrorFromString(errT.Error())
				}
				expireTsp := expire.Unix()

				if now >= expireTsp {
					// create new random password  for user,send it to user
					if len(user.Email) > 0 {
						randomPwd := vendor_account.RandomPwd()
						user.ResetPwd(randomPwd)
						user.UpdatedBy = user.Id
						user.PasswordStatus = 2
						err := s.repoService.UpdateUser(user)
						if err != nil {
							return nil,nil,nil,autherror.DBError(err.Error())
						}
						vendor_account.SendPwdResetEmail(user.Email,user.Name,randomPwd)
						return nil,nil,nil,autherror.NewHexError(string(enum.PassWordExpireError),"密码长时间未修改,系统已为您重新生成随机密码,并发送到您的邮箱 ")
					}
				}
			}
		}
	}



	pwdOk := user.ValidatePwd(pwd)
	if !pwdOk {
		utils.LoginErrorCounter.WithLabelValues("WRONG_PWD").Inc()

		if rule != nil && rule.LoginStatus == 2 {
			errS := s.cacheService.IncLoginWrongTimes(user.Id)
			if errS != nil && errS.Error() == string(enum.LoginWrongPasswordError) { //锁定十分钟
				s.cacheService.LockUser(user.Id)
				return nil,nil,nil,autherror.NewHexError(string(enum.UserLockedError),"该账户已经于五分钟内输入密码错误三次,已被锁定十分钟")
			}
		}

		return nil, nil, nil, autherror.NotFound("账户或密码错误！")
	}


	if ok := s.cacheService.ClearLoginWrongTimes(user.Id);!ok{
		log.Warnf("clear locked user fail")
	}

	if user.Status != enum.UserStatus_Active {
		if user.Status == enum.UserStatus_Deleted{
			return nil, nil, nil, autherror.PermissionDenied("该账户已被删除！")
		}
		return nil, nil, nil, autherror.PermissionDenied("该账户被禁用！")
	}
	if err := s.clearUserToken(context.Background(), user, true); err != nil {
		return nil, nil, nil, autherror.DBError("clear old token error")
	}
	token, hexErr := s.genBearerTokenForUser(client, user, state, scope, device)
	if hexErr != nil {
		return nil, nil, nil, hexErr
	}
	cacheErr := s.cacheService.SetToken(token)
	if cacheErr != nil {
		log.Error("set token cache error:", cacheErr)
	}
	ctx := context.WithValue(context.Background(), "partner_id", user.PartnerId)
	ctx = context.WithValue(ctx, "user_id", user.Id)
	// set user data to cache
	_, _ = s.GetUserDetailData(ctx, user.Id)
	return client, user, token, nil
}

type MiscellServiceInterface interface {
	JWTTokenSign(token *JWTToken) (tokenStr string, err error)
	JWTTokenVerify(tokenStr string) (*JWTToken, error)
}

func (s *OauthServiceImpl) genJWTTokenForUser(client *model.Client, user *model.User, state, scope string, grantType model.GrantType) (*model.Token, autherror.HexError) {
	if client.PartnerId != user.PartnerId {
		return nil, autherror.InvalidAction("client and user not belong to same partner")
	}
	if client.ScopeId != user.ScopeId {
		return nil, autherror.InvalidAction("client and user not belong to same scope")
	}
	expiresIn := time.Now().Add(config.AccessTokenLifetime)
	jwtToken := &JWTToken{
		UserId:    user.Id,
		PartnerId: user.PartnerId,
		ScopeId:   user.ScopeId,
		ExpiresAt: expiresIn.Unix(),
	}
	accessTokenStr, err := JWTTokenSign(jwtToken, s.jwtSecret)
	if err != nil {
		return nil, autherror.Unknown("sign jwt token fail")
	}
	if len(accessTokenStr) >= 255 {
		return nil, autherror.Unknown("jwt token len is bigger than 255")
	}
	hexToken := &model.Token{
		GrantType:        model.Implicit,
		TokenType:        model.JWT,
		ExpiresIn:        expiresIn,
		AccessToken:      accessTokenStr,
		RefreshToken:     accessTokenStr,
		RefreshExpiresIn: expiresIn,
		RequestState:     state,
		UserId:           user.Id,
		PartnerId:        user.PartnerId,
		ClientId:         client.Id,
		ScopeCode:        scope,
		ScopeId:          user.ScopeId,
	}

	err = s.repoService.AddToken(hexToken)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return hexToken, nil
}

func (s *OauthServiceImpl) Close() {
	s.repoService.Close()
}

func (s *OauthServiceImpl) Authenticate(ctx context.Context, req *params.AuthenticateRequest) (*params.AuthenticateResponse, autherror.HexError) {
	if len(req.Token) > 100 {
		return s.authenticateJWTToken(ctx, req)
	}
	if req.Token == "" {
		return nil, autherror.InvalidData("no token")
	}
	var resp = new(params.AuthenticateResponse)
	cacheToken := s.cacheService.GetToken(req.Token)
	cacheToken = nil
	if cacheToken != nil {
		resp.PartnerId = cacheToken.PartnerId
		resp.UserId = cacheToken.UserId
		resp.ScopeId = cacheToken.ScopeId
		resp.TerminalId = cacheToken.TerminalId
	} else {
		tokenObj := s.repoService.GetToken(req.Token)
		if tokenObj == nil {
			return nil, autherror.NotFound("can not find token")
		}
		fmt.Printf("expired in %v", tokenObj.ExpiresIn)
		if tokenObj.ExpiresIn.Before(time.Now()) {
			return nil, autherror.InvalidData("token has expired")
		}
		resp.PartnerId = tokenObj.PartnerId
		resp.UserId = tokenObj.UserId
		resp.ScopeId = tokenObj.ScopeId
		resp.TerminalId = tokenObj.TerminalId
		err := s.cacheService.SetToken(tokenObj)
		if err != nil {
			log.Error("set token cache fail: ", err)
		}
	}
	if !req.CheckPermission {
		return resp, nil
	}
	hasPermission, err := s.repoService.CheckAPIPermission(resp.PartnerId, resp.UserId, req.APICode)
	if err != nil {
		return nil, autherror.DBError("db error when check permission: " + err.Error())
	}
	if !hasPermission {
		return nil, autherror.PermissionDenied("no permission of " + req.APICode)
	}
	return resp, nil
}

func (s *OauthServiceImpl) authenticateJWTToken(ctx context.Context, req *params.AuthenticateRequest) (*params.AuthenticateResponse, autherror.HexError) {
	jwtToken, err := JWTTokenVerify(s.jwtSecret, req.Token)
	if err != nil {
		return nil, autherror.Unauthorized("jwt verify err: " + err.Error())
	}
	resp := &params.AuthenticateResponse{
		PartnerId: jwtToken.PartnerId,
		UserId:    jwtToken.UserId,
		ScopeId:   jwtToken.ScopeId,
	}
	if !req.CheckPermission {
		return resp, nil
	}
	hasPermission, err := s.repoService.CheckAPIPermission(jwtToken.PartnerId, jwtToken.UserId, req.APICode)
	if err != nil {
		return nil, autherror.DBError("db error when check permission: " + err.Error())
	}
	if !hasPermission {
		return nil, autherror.PermissionDenied("no permission of " + req.APICode)
	}
	return resp, nil
}

func (s *OauthServiceImpl) listUserAPIs(partnerId, userId uint64) ([]*model.API, error) {
	roleListReq := params.RoleListRequest{
		UserIds: []uint64{userId},
		ListRequest: params.ListRequest{
			PartnerId: partnerId,
		},
	}
	roles, _, err := s.repoService.ListRoles(&roleListReq)
	if err != nil {
		log.Error("list roles fail!")
		return nil, err
	}
	if len(roles) == 1 {
		return []*model.API{}, nil
	}
	var rolesIds = make([]uint64, len(roles))
	for i, role := range roles {
		rolesIds[i] = role.Id
	}
	permissionListReq := params.PermissionListRequest{
		ListRequest: params.ListRequest{
			PartnerId: partnerId,
		},
		RoleIds: rolesIds,
	}
	permissions, _, err := s.repoService.ListPermissions(&permissionListReq)
	if err != nil {
		log.Error("list permissions fail!")
		return nil, err
	}
	if len(permissions) == 0 {
		return []*model.API{}, nil
	}
	var permissionIds = make([]uint64, len(permissions))
	for i, permission := range permissions {
		permissionIds[i] = permission.Id
	}
	apiListReq := params.APIListRequest{
		ListRequest: params.ListRequest{
			PartnerId: partnerId,
		},
		PermissionIds: permissionIds,
	}
	apis, _, err := s.repoService.ListAPIs(&apiListReq)
	if err != nil {
		log.Error("list apis fail!")
		return nil, err
	}
	return apis, nil
}

func (s *OauthServiceImpl) getUserPermissionCodes(ctx context.Context) {

}
func (s *OauthServiceImpl) GetUserRoles(partnerId, userId uint64) []*model.Role {
	return s.repoService.GetUserRoles(partnerId, userId)
}

//func (s *OauthServiceImpl)
func (s *OauthServiceImpl) GetRolesPermissions(partnerId uint64, rolesIds []uint64) []*model.Permission {
	req := &params.PermissionListRequest{
		RoleIds: rolesIds,
	}
	req.ListRequest = params.ListRequest{
		PartnerId: partnerId,
	}
	permissions, _, _ := s.repoService.ListPermissions(req)
	return permissions
}

func (s *OauthServiceImpl) GetUserDetailData(ctx context.Context, userId uint64) (data *model.UserDetailData, hexErr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	data, err := s.repoService.GetUserDetailData(pId, userId)
	if err != nil {
		return nil, autherror.DBError("get user detail data error :" + err.Error())
	}
	return data, nil
}

func (s *OauthServiceImpl) GetUserWithPreload(ctx context.Context, userId uint64) (*model.User, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	user, err := s.repoService.GetUserDetailFromRDB(pId, userId)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return user, nil
}

func (s *OauthServiceImpl) GetRoleDetail(ctx context.Context, roleId uint64) (*model.Role, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	role := s.repoService.GetRoleById(roleId)
	if role == nil {
		return nil, autherror.NotFound("can't found role")
	}
	if role.PartnerId != pId {
		return nil, autherror.PermissionDenied("wrong partner")
	}
	permissionInfos, err := s.repoService.ListRolePermissionInfos(role.Id)
	if err != nil {
		return nil, autherror.NotFound("select permission infos fail")
	}
	role.PermissionInfos = permissionInfos
	return role, nil
}

func (s *OauthServiceImpl) ListUser(ctx context.Context, req *params.UserListRequest) (users []*model.User, total uint64, hexerr autherror.HexError) {
	scopeId := ctx.Value("scope_id").(uint64)
	pId := ctx.Value("partner_id").(uint64)
	req.ScopeId = scopeId
	req.PartnerId = pId
	req.Types = []enum.UserType{enum.UserType_Staff,enum.UserType_VendorManager,enum.UserType_VendorUser}
	req.Statuses = []enum.UserStatus{enum.UserStatus_Active,enum.UserStatus_Forbidden}
	users, total, err := s.repoService.ListUser(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	return users, total, nil
}

func (s *OauthServiceImpl) UpdateUser(ctx context.Context, req *params.UserUpdateRequest) (*model.User, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	user := s.repoService.GetUserByIdFromDB(req.Id)
	if user == nil {
		return nil, autherror.NotFound("can not find user")
	}
	if user.PartnerId != pId {
		return nil, autherror.PermissionDenied("wrong partner")
	}
	if req.IsLocked != nil {
		user.IsLocked = *req.IsLocked
	}
	if req.IsSystem != nil {
		user.IsSystem = *req.IsSystem
	}
	if req.Remark != nil {
		user.Remark = *req.Remark
	}
	if len(req.BindStatus) > 0 {
		bindStatus := enum.BindStatus(req.BindStatus)
		err := bindStatus.Valid();if err != nil{
			return nil,err
		}
		user.BindStatus = bindStatus
	}
	if len(req.Name) != 0 {
		user.Name = req.Name
		listReq := &params.UserListRequest{
			ScopeId: scopeId,
			ListRequest: params.ListRequest{
				PartnerId: pId,
				Exclude: params.Exclude{
					Id: user.Id,
				},
			},
		}
		listReq.PartnerId = pId
		listReq.Name = req.Name
		listReq.Limit = 1
		existUsers, _, _ := s.repoService.ListUser(listReq)
		if len(existUsers) != 0 {
			return nil, autherror.InvalidData("name already exist")
		}
	}
	if len(req.Code) != 0 {
		user.Code = req.Code
		listReq := &params.UserListRequest{
			ListRequest: params.ListRequest{
				PartnerId: pId,
				Exclude: params.Exclude{
					Id: user.Id,
				},
			},
		}
		listReq.PartnerId = pId
		listReq.Code = req.Code
		listReq.Limit = 1
		existUsers, _, _ := s.repoService.ListUser(listReq)
		if len(existUsers) != 0 {
			return nil, autherror.InvalidData("code already exist")
		}
	}

	if len(req.LanType) != 0 {
		user.LanType = req.LanType
	}

	var pwdChange bool

	ruleCfg := config.LoadPassWdRuleConfig()
	rule := &model.PassWordRule{}
	if ruleCfg.Enable {
		ruleR, errR := s.repoService.GetPasswordRule();if errR != nil {
			return nil, autherror.DBError("get password rule fail")
		}
		rule = ruleR
	}

	if req.NewPwd != "" {
		isNew :=ctx.Value("is_new").(bool)
		if !isNew { // 2019-09-23 disable old password required
			pwdOk := user.ValidatePwd(req.OldPwd)
			if !pwdOk {
				return nil, autherror.WrongPassword("The old password is wrong")
			}
		}
		//if err := utils.CheckStrPwdQualify(req.NewPwd); err != nil {
		//	return nil, autherror.InvalidData(err.Error())
		//}
		if rule != nil{
			valid :=  rule.Verify(req.NewPwd)
			if !valid {
				return nil, autherror.NewHexError(string(enum.PassWordNotFitRule),"当前的密码不符合管理员设置的密码规则")
			}

			if err := utils.CheckStrPwdQualify(req.NewPwd); err != nil {
					return nil, autherror.InvalidData(err.Error())
			}
		}

		user.ResetPwd(req.NewPwd)
		pwdChange = true
	}
	user.UpdatedBy = ctx.Value("user_id").(uint64)
	user.PasswordStatus = 2
	err := s.repoService.UpdateUser(user)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if pwdChange {
		err := s.clearUserToken(ctx, user, false)
		if err != nil {
			log.WithField("user_id", user.Id).WithError(err).Error("clear user token fail")
		}
	}
	return user, nil
}

func (s *OauthServiceImpl) clearUserToken(ctx context.Context, user *model.User, onlyExpiredToken bool) autherror.HexError {
	if onlyExpiredToken {
		expired := true
		dbErr := s.repoService.DeleteTokens(&params.DeleteTokenOption{
			PartnerId: user.PartnerId,
			UserId:    user.Id,
			Expired:   &expired,
		})
		if dbErr != nil {
			return autherror.DBError(dbErr.Error())
		} else {
			return nil
		}
	} else {
		expired := false
		listReq := &params.TokenListRequest{
			PartnerId: user.PartnerId,
			UserId:    user.Id,
			ScopeId:   user.ScopeId,
			Expired:   &expired,
		}
		unExpiredtokens, err := s.repoService.ListToken(listReq)
		if err != nil {
			return autherror.DBError(err.Error())
		}
		deleteOpt := &params.DeleteTokenOption{
			PartnerId: user.PartnerId,
			UserId:    user.Id,
		}
		dbErr := s.repoService.DeleteTokens(deleteOpt)
		if dbErr != nil {
			return autherror.DBError(dbErr.Error())
		}
		if len(unExpiredtokens) == 0 {
			return nil
		}
		accessTokens := make([]string, len(unExpiredtokens))
		for i, token := range unExpiredtokens {
			accessTokens[i] = token.AccessToken
		}
		err1 := s.cacheService.ClearTokens(accessTokens...)
		err2 := s.cacheService.ClearUserToken(user.Id)
		if err1 != nil {
			return autherror.DBError("clear token cache: " + err1.Error())
		}
		if err2 != nil {
			return autherror.DBError("clear token cache: " + err2.Error())
		}
	}
	return nil
}

func (s *OauthServiceImpl) DeleteUser(ctx context.Context, userId uint64) autherror.HexError {
	pId := ctx.Value("partner_id").(uint64)
	user := s.repoService.GetUserById(userId)
	if user == nil {
		return autherror.NotFound("can't find user")
	}
	if user.PartnerId != pId {
		return autherror.PermissionDenied("no permission to delete this user")
	}
	err := s.repoService.DeleteUser(userId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) AddUser(ctx context.Context, req *params.UserCreateRequest) (
	*model.User, autherror.HexError) {
	if len(req.Name) == 0 || len(req.Code) == 0 {
		return nil, autherror.InvalidData("name or code can't be empty")
	}
	if err := utils.CheckStrPwdQualify(req.Pwd); err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	req.ScopeId = scopeId

	listReq := &params.UserListRequest{}
	listReq.PartnerId = pId
	listReq.ScopeId = scopeId
	listReq.Name = req.Name
	listReq.Limit = 1
	duplicateNameUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateNameUsers) != 0 {
		return nil, autherror.InvalidData("name already exist")
	}
	listReq.Name = ""
	listReq.Code = req.Code
	duplicateCodeUsers, _, _ := s.repoService.ListUser(listReq)
	if len(duplicateCodeUsers) != 0 {
		return nil, autherror.InvalidData("code already exist")
	}
	user := &model.User{
		Name:      req.Name,
		Code:      req.Code,
		Remark:    req.Remark,
		ScopeId:   req.ScopeId,
		PartnerId: pId,
		LanType: req.LanType,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: uId,
		},
		Type:   enum.UserType_Staff,
		Status: enum.UserStatus_Active,
	}
	if req.IsLocked != nil {
		user.IsLocked = *req.IsLocked
	}
	if req.IsSystem != nil {
		user.IsSystem = *req.IsSystem
	}
	user.ResetPwd(req.Pwd)
	err := s.repoService.AddUser(user)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return user, nil
}

func (s *OauthServiceImpl) ListRoles(ctx context.Context, req *params.RoleListRequest) ([]*model.Role, uint64, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	scopId, ok := ctx.Value("scope_id").(uint64)
	if !ok {
		scopId = 0
	}
	req.PartnerId = pId
	req.ScopeId = scopId
	roles, total, err := s.repoService.ListRoles(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	if req.LanType != ``{
		for _, role := range roles {
			lanName := cast.ToString(role.Content[lanType])
			if lanName != ``{
				role.Name = lanName
			}
		}
	}
	return roles, total, nil
}

func (s *OauthServiceImpl) AddRole(ctx context.Context, req *params.RoleCreateRequest) (*model.Role, autherror.HexError) {
	if len(req.Name) == 0 || len(req.Code) == 0 {
		return nil, autherror.InvalidData("name and code can't be empty")
	}
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	req.ScopeId = scopeId

	listReq := &params.RoleListRequest{}
	listReq.PartnerId = pId
	listReq.ScopeId = scopeId
	listReq.Name = req.Name
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	if lanType == ``{
		nameDuplicateRoles, _, _ := s.repoService.ListRoles(listReq)
		if len(nameDuplicateRoles) != 0 {
			return nil, autherror.InvalidData("name already exist")
		}
	}else{
		role := s.repoService.GetPermissionByLan(lanType,req.Name)
		if role != nil{
			return nil, autherror.InvalidData(fmt.Sprintf("lan_type:%s name:%s already exist",req.LanType,req.Name))
		}
	}

	listReq.Name = ""
	listReq.Code = req.Code
	codeDuplicateRoles, _, _ := s.repoService.ListRoles(listReq)
	if len(codeDuplicateRoles) != 0 {
		return nil, autherror.InvalidData("code already exist")
	}
	role := &model.Role{
		PartnerId:   pId,
		Code:        req.Code,
		Description: req.Description,
		ScopeId:     scopeId,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: uId,
		},
	}
	if lanType == ``{
	    role.Name = req.Name
	}
	if lanType != ``{
		content := model.Json{
			lanType: req.Name,
		}
		role.Content = content
	}
	err := s.repoService.AddRole(role)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return role, nil
}

func (s *OauthServiceImpl) UpdateRole(ctx context.Context, req *params.RoleUpdateRequest) (*model.Role, autherror.HexError) {
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	pId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	role := s.repoService.GetRoleById(req.Id)
	if role == nil {
		des := fmt.Sprintf("not find role %v", req.Id)
		return nil, autherror.NotFound(des)
	}

	roleListReq := &params.RoleListRequest{
		ScopeId: scopeId,
		ListRequest: params.ListRequest{
			PartnerId: pId,
			Exclude: params.Exclude{
				Id: req.Id,
			},
		},
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	if lanType == ``{
		roleListReq.Name = req.Name
		_, nameDuplicateCount, err := s.repoService.ListRoles(roleListReq)
		if err != nil {
			return nil, autherror.DBError(err.Error())
		}
		if nameDuplicateCount != 0 {
			return nil, autherror.InvalidData("name already exist")
		}
	}else{
		r := s.repoService.GetPermissionByLan(lanType,req.Name);if r != nil{
			return nil, autherror.InvalidData(fmt.Sprintf("lan_type:%s name:%s already exist",req.LanType,req.Name))
		}
	}
	roleListReq.Name = ``
	roleListReq.Code = req.Code
	_, codeDuplicateCount, err := s.repoService.ListRoles(roleListReq)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if codeDuplicateCount != 0 {
		return nil, autherror.InvalidData("code already exist")
	}

	role.Code = req.Code
	if lanType == ``{
		role.Name = req.Name
	}else{
		content := model.Json{}
		content[lanType] = req.Name
		targetContent := role.Content.Merge(content)
		role.Content = targetContent
	}
	err = s.repoService.UpdateRole(role)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return role, nil
}

func (s *OauthServiceImpl) DeleteRole(ctx context.Context, roleId uint64) autherror.HexError {
	role := s.repoService.GetRoleById(roleId)
	if role == nil {
		return autherror.NotFound(fmt.Sprintf("can't found role %v", roleId))
	}
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	if role.PartnerId != pId {
		warn := fmt.Sprintf("some one (pId %v, uId %v) attemp to delete "+
			"role not belong to his partner(pid: %v)", pId, uId, role.PartnerId)
		log.Warn(warn)
		return autherror.PermissionDenied("no permission")
	}
	err := s.repoService.DeleteRole(role.Id)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	err = s.repoService.ReplaceRolePermissionInfos(role, make([]*model.RolePermissionInfo, 0))
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) AddPermission(ctx context.Context, req *params.PermissionCreateRequest) (*model.Permission, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	uId := ctx.Value("user_id").(uint64)
	err := req.Validate()
	if err != nil {
		return nil, autherror.InvalidData(err.Error())
	}

	permission := &model.Permission{}
	permission.PartnerId = pId
	listReq := &params.PermissionListRequest{
		ListRequest: params.ListRequest{
			PartnerId: pId,
		},
		Name: req.Name,
	}
	_, nameDuplicateCount, _ := s.repoService.ListPermissions(listReq)
	if nameDuplicateCount != 0 {
		return nil, autherror.InvalidData("name already exist")
	}
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeDuplicateCount, _ := s.repoService.ListPermissions(listReq)
	if codeDuplicateCount != 0 {
		return nil, autherror.InvalidData("code already exist")
	}
	permission = &model.Permission{
		Name:        req.Name,
		Code:        req.Code,
		Description: req.Description,
		Remark:      req.Remark,
		MyGormModel: model.MyGormModel{
			CreatedBy: uId,
			UpdatedBy: pId,
		},
	}

	err = s.repoService.AddPermission(permission)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return permission, nil
}

func (s *OauthServiceImpl) ListPermission(ctx context.Context, req *params.PermissionListRequest) (
	permis []*model.Permission, total uint64, hexerr autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	req.ListRequest.PartnerId = pId
	permis, total, err := s.repoService.ListPermissions(req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	lanType := strings.ToLower(strings.TrimSpace(req.LanType))
	if lanType == ``{
		return permis, total, nil
	}
	for _, item := range permis {
		lanName := item.Content[lanType]
		if lanName != nil{
			item.Name = cast.ToString(lanName)
		}
	}
	return permis, total, nil
}

func (s *OauthServiceImpl) UpdatePermission(ctx context.Context, req *params.PermissionUpdateRequest) (*model.Permission, autherror.HexError) {
	partnerId := ctx.Value("partner_id").(uint64)
	permission := s.repoService.GetPermissionById(req.Id)
	if permission == nil {
		return nil, autherror.NotFound("can't find permission")
	}
	if permission.PartnerId != partnerId {
		return nil, autherror.PermissionDenied("no permission")
	}

	listReq := &params.PermissionListRequest{
		ListRequest: params.ListRequest{
			PartnerId: partnerId,
			Exclude: params.Exclude{
				Id: req.Id,
			},
		},
		Name: req.Name,
	}
	_, nameCount, err1 := s.repoService.ListPermissions(listReq)
	listReq.Name = ""
	listReq.Code = req.Code
	_, codeCount, err2 := s.repoService.ListPermissions(listReq)

	if err1 != nil {
		return nil, autherror.DBError(err1.Error())
	}
	if err2 != nil {
		return nil, autherror.DBError(err2.Error())
	}
	if codeCount != 0 {
		return nil, autherror.InvalidData("code already exist")
	}
	if nameCount != 0 {
		return nil, autherror.InvalidData("name already exist")
	}

	permission.Name = req.Name
	permission.Code = req.Code
	err := s.repoService.UpdatePermission(permission)
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return permission, nil
}

func (s *OauthServiceImpl) AddPermissionAPIs(ctx context.Context, req *params.PermissionAddAPIsRequest) autherror.HexError {
	partnerId := ctx.Value("partner_id").(uint64)
	permission := s.repoService.GetPermissionById(req.Id)
	if permission == nil {
		return autherror.NotFound("can't find permission")
	}
	if permission.PartnerId != partnerId {
		return autherror.PermissionDenied("no permission")
	}

	var apis = make([]*model.API, 0)
	if len(req.APIIds) != 0 {
		apis, _, _ = s.repoService.ListAPIs(&params.APIListRequest{
			ListRequest: params.ListRequest{
				Ids: req.APIIds,
			},
		})
	}
	for _, api := range apis {
		if api.PartnerId != permission.PartnerId {
			return autherror.PermissionDenied("api and permission not same partner")
		}
	}
	err := s.repoService.ReplacePermissionAPIs(permission, apis)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) AddUserRole(ctx context.Context, req *params.UserAddRoleRequest) autherror.HexError {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	user := s.repoService.GetUserById(req.Id)
	if user == nil {
		return autherror.NotFound("can't find user")
	}
	if user.PartnerId != partnerId {
		return autherror.PermissionDenied("no partner permission")
	}
	if user.ScopeId != scopeId {
		return autherror.PermissionDenied("no scope permission")
	}
	var roles = make([]*model.Role, 0)
	if len(req.RoleIds) != 0 {
		roles, _, _ = s.repoService.ListRoles(
			&params.RoleListRequest{
				ScopeId: scopeId,
				ListRequest: params.ListRequest{
					Ids: req.RoleIds,
				},
			})
	}
	for _, r := range roles {
		if r.PartnerId != user.PartnerId {
			return autherror.PermissionDenied("user and role not in same partner")
		}
	}
	err := s.repoService.ReplaceUserRoles(user, roles)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) DeletePermission(ctx context.Context, permissionId uint64) autherror.HexError {
	partnerId := ctx.Value("partner_id").(uint64)
	permission := s.repoService.GetPermissionById(permissionId)
	if permission == nil {
		return autherror.NotFound("can't find permission")
	}
	if permission.PartnerId != partnerId {
		return autherror.PermissionDenied("no permission")
	}
	err := s.repoService.DeletePermission(permissionId)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) GetPermissionDetail(ctx context.Context, permissionId uint64) (*model.Permission, autherror.HexError) {
	partnerId := ctx.Value("partner_id").(uint64)
	permission := s.repoService.GetPermissionById(permissionId)
	if permission == nil {
		return nil, autherror.NotFound("can't find permission")
	}
	if partnerId != permission.PartnerId {
		return nil, autherror.PermissionDenied("wrong partner")
	}
	apis, _, err := s.repoService.ListAPIs(
		&params.APIListRequest{
			ListRequest: params.ListRequest{
				PartnerId: partnerId,
			},
			PermissionIds: []uint64{permissionId},
		})
	if err != nil {
		return nil, autherror.DBError(err.Error())
	}
	permission.APIs = apis
	return permission, nil
}

func (s *OauthServiceImpl) DeviceExist(ctx context.Context, uuid string) (bool, autherror.HexError) {
	device := s.repoService.GetDeviceByUUID(uuid)
	if device == nil {
		return true, nil
	}
	return false, nil
}

func (s *OauthServiceImpl) GetDeviceById(ctx context.Context, deviceId uint64) (*model.Device, autherror.HexError) {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	device := s.repoService.GetDeviceById(deviceId)
	if device == nil {
		return nil, autherror.NotFound("no device found")
	}
	if device.PartnerId != partnerId && device.ScopeId != scopeId {
		return nil, autherror.PermissionDenied("wrong partner or scope")
	}
	return device, nil
}

func (s *OauthServiceImpl) CheckDeviceExist(ctx context.Context, uuid string) (*model.Device, autherror.HexError) {
	device := s.repoService.GetDeviceByUUID(uuid)
	if device == nil {
		return nil, autherror.NotFound("no device found")
	}
	return device, nil
}

func (s *OauthServiceImpl) AddDevice(ctx context.Context, req *params.DeviceCreateRequest) (*model.Device, autherror.HexError) {
	if err := req.Validate(); err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	client := s.repoService.GetClientById(req.ClientId)
	if client == nil {
		return nil, autherror.NotFound("can not found client")
	}
	existDevice := s.repoService.GetDeviceByUUID(req.UUID)
	if existDevice != nil {
		return nil, autherror.InvalidData("device has exist!")
	}
	device := &model.Device{
		PartnerId:     client.PartnerId,
		ScopeId:       client.ScopeId,
		UUID:          req.UUID,
		Status:        enum.DeviceStatus_Init,
		Remark:        req.Remark,
		Manufacturer:  req.Manufacturer,
		Brand:         req.Brand,
		Model:         req.Model,
		SystemName:    req.SystemName,
		SystemVersion: req.SystemVersion,
	}
	now := time.Now()
	device.CreatedAt = now
	device.UpdatedAt = now
	if err := s.repoService.AddDevice(device); err != nil {
		return nil, autherror.DBError(err.Error())
	}
	return device, nil
}

func (s *OauthServiceImpl) UpdateDevice(ctx context.Context, deviceId uint64, req *params.DeviceUpdateRequest) (*model.Device, autherror.HexError) {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	userId := ctx.Value("user_id").(uint64)
	if err := req.Validate(); err != nil {
		return nil, autherror.InvalidData(err.Error())
	}
	device := s.repoService.GetDeviceById(deviceId)
	if device == nil {
		return nil, autherror.NotFound("device not exist")
	}
	if device.PartnerId != partnerId && device.ScopeId != scopeId {
		return nil, autherror.PermissionDenied("wrong partner or scope")
	}
	if req.UUID != nil {
		existDevice := s.repoService.GetDeviceByUUID(*req.UUID)
		if existDevice.Id != device.Id {
			return nil, autherror.InvalidData("uuid is duplicate with other device")
		}
		device.UUID = *req.UUID
	}
	var forbiddenDevice bool
	if req.Status != nil {
		device.Status = enum.DeviceStatus(*req.Status)
		if device.Status == enum.DeviceStatus_Forbidden {
			forbiddenDevice = true
		}
	}
	if req.Remark != nil {
		device.Remark = *req.Remark
	}
	if req.Manufacturer != nil {
		device.Manufacturer = *req.Manufacturer
	}
	if req.Brand != nil {
		device.Brand = *req.Brand
	}
	if req.Model != nil {
		device.Model = *req.Model
	}
	if req.SystemName != nil {
		device.SystemName = *req.SystemName
	}
	if req.SystemVersion != nil {
		device.SystemVersion = *req.SystemVersion
	}
	device.UpdatedBy = userId
	device.UpdatedAt = time.Now()
	if err := s.repoService.UpdateDevice(device); err != nil {
		return nil, autherror.DBError(err.Error())
	}
	if forbiddenDevice {
		if err := s.clearDeviceToken(ctx, device.UUID); err != nil {
			return nil, err
		}
	}
	return device, nil
}

func (s *OauthServiceImpl) DeleteDevice(ctx context.Context, deviceId uint64) autherror.HexError {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	device := s.repoService.GetDeviceById(deviceId)
	if device == nil {
		return autherror.NotFound("device not exist")
	}
	if device.PartnerId != partnerId && device.ScopeId != scopeId {
		return autherror.PermissionDenied("wrong partner or scope")
	}

	if err := s.repoService.DeleteDevice(device.Id); err != nil {
		return autherror.DBError(err.Error())
	}
	if err := s.clearDeviceToken(ctx, device.UUID); err != nil {
		return err
	}
	return nil
}

func (s *OauthServiceImpl) ListDevice(ctx context.Context, req *params.DeviceListRequest) ([]*model.Device, uint64, autherror.HexError) {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	req.PartnerId = partnerId
	devices, total, err := s.repoService.ListDevice(partnerId, scopeId, req)
	if err != nil {
		return nil, 0, autherror.DBError(err.Error())
	}
	return devices, total, nil
}

func (s *OauthServiceImpl) clearDeviceToken(ctx context.Context, deviceUUID string) autherror.HexError {
	partnerId := ctx.Value("partner_id").(uint64)
	scopeId := ctx.Value("scope_id").(uint64)
	expired := false
	listReq := &params.TokenListRequest{
		PartnerId:  partnerId,
		ScopeId:    scopeId,
		DeviceUUID: deviceUUID,
		Expired:    &expired,
	}
	tokens, err := s.repoService.ListToken(listReq)
	if err != nil {
		return autherror.DBError(err.Error())
	}
	deleteOpt := &params.DeleteTokenOption{
		PartnerId:  partnerId,
		ScopeId:    scopeId,
		DeviceUUID: deviceUUID,
	}
	if err := s.repoService.DeleteTokens(deleteOpt); err != nil {
		return autherror.DBError(err.Error())
	}
	if len(tokens) == 0 {
		return nil
	}
	var accessTokens = make([]string, len(tokens))
	for i, token := range tokens {
		accessTokens[i] = token.AccessToken
	}
	if err := s.cacheService.ClearTokens(accessTokens...); err != nil {
		return autherror.DBError("clear token cache: " + err.Error())
	}
	return nil
}

func (s *OauthServiceImpl) ResetUserPassWdPre(ctx context.Context, req *params.UserPassWdMailReq) autherror.HexError {
	if len(req.Mail) == 0 {
		return autherror.InvalidData("mail can not be black!!!")
	}
	user := s.repoService.GetUserByIdFromDB(req.UserId)
	if user == nil {
		return  autherror.NotFound("can not find user")
	}

	passed := utils.CheckEmailFormat(req.Mail)
	if !passed {
		return autherror.InvalidData("mail format is  incorrect!!!")
	}

	codeStr,errS := s.cacheService.CreateTtlCode(req.UserId,req.Mail)
	if errS != nil{
		return autherror.HexErrorFromString(errS.Error())
	}

	// send mail to user
	mailMsg := utils.MailMessage{
		ToUser:   req.Mail,
		AuthCode:  codeStr,
		Subject:  "重置密码",
		Domain:  req.Host,
		Account: user.Name,
		Mail:	user.Email,
		UserId: user.Id,
	}
	errU := utils.SendHtmlEmail(&mailMsg)
	if errU != nil{
		return autherror.HexErrorFromString(errU.Error())
	}
	return nil
}

func (s *OauthServiceImpl) ResetPassWdByNew(ctx context.Context, req *params.UserNewPassWdResetReq) autherror.HexError {
	if len(req.Code) == 0 {
		return autherror.HexErrorFromString("auth code can not be blank!!!")
	}
	if len(req.PassWord) == 0 {
		return autherror.HexErrorFromString("new password can not be blank!!!")
	}
	authCode := s.cacheService.GetCode(req.UserId,req.Mail)
	if len(authCode) == 0 {
		return autherror.HexErrorFromString("auth code has expired!!!")
	}
	log.Info("authCode:",authCode)
	if authCode != req.Code {
		return autherror.HexErrorFromString("auth code is incorrect!!!")
	}

	user := s.repoService.GetUserByIdFromDB(req.UserId)
	if user == nil {
		return  autherror.NotFound("can not find user")
	}

	if err := utils.CheckStrPwdQualify(req.PassWord); err != nil {
		return  autherror.InvalidData(err.Error())
	}
	user.ResetPwd(req.PassWord)

	user.UpdatedBy = req.UserId
	err := s.repoService.UpdateUser(user)
	if err != nil {
		return  autherror.DBError(err.Error())
	}

	errC := s.clearUserToken(ctx, user, false)
	if errC != nil {
		log.WithField("user_id", user.Id).WithError(err).Error("clear user token fail")
	}
	return nil
}

func (s *OauthServiceImpl) CheckMail(ctx context.Context, req *params.UserPassWdMailReq) (*model.User,autherror.HexError) {
	passed := utils.CheckEmailFormat(req.Mail)
	if !passed {
		return  nil,autherror.InvalidData("邮箱格式不正确")
	}

	user := s.repoService.GetUserByMailFromDB(req.Mail)
	if user == nil {
		return nil, autherror.NotFound("没有该用户")
	}

	if user.Email != req.Mail {
		return nil, autherror.NotFound("邮箱不正确")
	}

	return user,nil
}

func (s *OauthServiceImpl) AddUserMailRule(ctx context.Context, req *params.UserPassWdRuleReq) (bool, autherror.HexError) {
	if req.PasswordForm.BigLetter{
		if !req.PasswordForm.Letter{
			return false,autherror.NewHexError(string(enum.PassWordNotFitRule),"当前的密码不符合管理员设置的密码规则")
		}
	}
	rule := &model.PassWordRule{
		UserId:req.UserId,
	}
	formMap,err := utils.ObjectToMap(req.PasswordForm)
	if  err != nil{
		return false,autherror.HexErrorFromString(err.Error())
	}
	rule.PassWordForm = formMap

	lengthMap,errL := utils.ObjectToMap(req.PasswordLength)
	if errL != nil{
		return false,autherror.InvalidData(errL.Error())
	}
	rule.PassWordLength = lengthMap
	rule.LoginStatus = req.LoginStatus
	rule.ExpireDay = req.ExpireDay
	rule.ExpireMonth = req.ExpireMonth
	//rule.PassWordExpire = utils.MsToString(req.PasswordExp)


	err = s.repoService.CreatePasswordRule(rule)
	if err != nil{
		return false,autherror.DBError(err.Error())
	}

	return true,nil
}

func (s *OauthServiceImpl) GetUserMailRule(ctx context.Context,userId uint64) (*model.PassWordRule, autherror.HexError) {
	user := s.repoService.GetUserByIdFromDB(userId)
	if user == nil {
		return nil, autherror.NotFound("没有此用户")
	}

	//if !user.IsSystem {
	//	return nil, autherror.PermissionDenied("权限不足")
	//}

	rule,err :=	s.repoService.GetPasswordRule()
	if err != nil{
		return nil,autherror.HexErrorFromString(err.Error())
	}

	return rule,nil
}

func (s *OauthServiceImpl) UpdateUserMailRule(ctx context.Context, req  *params.UserPassWdRuleReq) (bool, autherror.HexError) {
	if req.PasswordForm.BigLetter {
		if !req.PasswordForm.Letter{
			return false,autherror.NewHexError(string(enum.PassWordNotFitRule),"当前的密码不符合管理员设置的密码规则")
		}
	}
	rule := &model.PassWordRule{
		UserId:req.UserId,
	}
	formMap,err := utils.ObjectToMap(req.PasswordForm)
	if  err != nil{
		return false,autherror.InvalidData(err.Error())
	}
	if len(formMap) > 0 {
		rule.PassWordForm = formMap
	}

	lengthMap,errL := utils.ObjectToMap(req.PasswordLength)
	if errL != nil{
		return false,autherror.InvalidData(errL.Error())
	}
	if len(lengthMap) > 0 {
		rule.PassWordLength = lengthMap
	}

	if req.LoginStatus > 0 {
		rule.LoginStatus = req.LoginStatus
	}

	if req.ExpireDay > 0 {
		rule.ExpireDay = req.ExpireDay
	}

	if req.ExpireMonth > 0 {
		rule.ExpireMonth = req.ExpireMonth
	}

	ruleId,_ := strconv.ParseUint(req.RuleId,10,64)
	updated,errU := s.repoService.UpdatePasswordRule(ruleId,rule)
	if errU != nil{
		return false,autherror.DBError(errU.Error())
	}

	return updated,nil
}


func (s *OauthServiceImpl) GetCommonRule(ctx context.Context) (*model.PassWordRule, autherror.HexError) {
	rule,err :=	s.repoService.GetPasswordRule()
	if err != nil{
		return nil,autherror.NotFound(err.Error())
	}
	return rule,nil
}


func (s *OauthServiceImpl) ForceUpdatePassWord(ctx context.Context, req *params.ResetPassWordReq) (string, autherror.HexError) {

	if len(req.IdList) == 0 {
		return "",autherror.InvalidData("id_list can not be blank")
	}
	password := ""
	if req.Type == 0 { // 随机
		password = vendor_account.RandomPwd()
	}
	if req.Type == 1 { // 手动
		rule,err := s.repoService.GetPasswordRule()
		if err != nil{
			log.Errorf("get rule fail:",err.Error())
		}
		if rule != nil{
			valid :=  rule.Verify(req.PassWd)
			if !valid {
				return "", autherror.NewHexError(string(enum.PassWordNotFitRule),"当前的密码不符合管理员设置的密码规则")
			}
		}

		password = req.PassWd
	}
	wg := sync.WaitGroup{}
	wg.Add(len(req.IdList))
	for _, idStr := range req.IdList {
		go func(id string) {
			defer func() {
				 wg.Done()
			}()
			uid,_ := strconv.ParseUint(id,10,64)
			user := s.repoService.GetUserByIdFromDB(uid)
			user.ResetPwd(password)
			user.UpdatedBy = ctx.Value("user_id").(uint64)
			user.PasswordStatus = 2
			err := s.repoService.UpdateUser(user)
			if err!= nil {
				log.Errorf("err",err)
			}
			if err == nil{
				errs := s.clearUserToken(ctx, user, false)
				if errs != nil {
					log.WithField("user_id", user.Id).WithError(err).Error("clear user token fail")
				}
			}
		}(idStr)
	}
	wg.Wait()

	return password,nil
}


func (s *OauthServiceImpl) DisableUser(ctx context.Context, req *params.UserDisableRequest) (bool, autherror.HexError) {
	pId := ctx.Value("partner_id").(uint64)
	user := s.repoService.GetUserByIdFromDB(req.Id)
	if user == nil {
		return false, autherror.NotFound("can not find user")
	}
	if user.PartnerId != pId {
		return false, autherror.PermissionDenied("wrong partner")
	}

	user.Status = req.Status
	user.UpdatedBy = ctx.Value("user_id").(uint64)
	if req.Status == enum.UserStatus_Deleted{
		now := time.Now()
		user.DeletedAt = &now
	}

	err := s.repoService.UpdateUser(user)
	if err != nil {
		return false, autherror.DBError(err.Error())
	}

	err = s.clearUserToken(ctx, user, false)
	if err != nil {
		log.WithField("user_id", user.Id).WithError(err).Error("clear user token fail")
	}


	return true,nil

}

func (s *OauthServiceImpl) BatchReplacePermissionMethod(ctx context.Context, req params.PermissionMethodRequest) autherror.HexError {
	var partnerId uint64
	md,ok := metadata.FromIncomingContext(ctx);if ok{
		partnerId = cast.ToUint64(md.Get(`partner_id`)[0])
	}

	entityList := make([]*model.Method,0)
	idList,_:= utils.GetIds(len(req.MethodList))

	for idx, item := range req.MethodList {
		entity := &model.Method{}
		entity.Id = idList[idx]
		entity.PartnerId = partnerId
		entity.ServiceName = req.ServiceName
		entity.MethodType = item.MethodType
		entity.MethodName = item.MethodName
		entity.Url = item.Url
		now := time.Now()
		entity.CreatedAt = now
		entity.DeletedAt = &now
		entityList = append(entityList,entity)
	}
	err := s.repoService.BatchAddPermissionMethod(partnerId,req.ServiceName,entityList);if err != nil{
		return autherror.HexErrorFromString(err.Error())
	}

	return nil
}

